#ifndef _SINKER_HPP_
#define _SINKER_HPP_

#include "util.hpp"
#include "config.hpp"
#include <memory>
#include <cassert>

namespace __mylog
{
    /*日志落地器基类，负责将日志消息输出到目标位置。*/
    class Sinker
    {
    public:
        using ptr = std::shared_ptr<Sinker>;
        virtual bool run(const char *message, size_t size) = 0;
        virtual ~Sinker() {}
    };

    /*控制台日志落地器，负责将日志消息输出到标准输出*/
    class ConsoleSinker : public Sinker
    {
    public:
        virtual bool run(const char *message, size_t size) override
        {
            std::cout.write(message, size);
            return std::cout.good();
        }
    };

    /*文件日志落地器，负责将日志消息输出到指定文件*/
    class FileSinker : public Sinker
    {
    public:
        // "./path/to/file"
        // "/path/to/file"
        // 提取路径，如果实际没有路径的话要创建路径
        FileSinker(const std::string &file)
            : _file(file)
        {
            // 在打开文件之前，要创建目录，因为可能目录不存在
            if(!FileUtil::exists(FileUtil::getPath(_file)))
                FileUtil::createDir(_file);

            // 以追加方式打开文件
            _ofs.open(_file, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        ~FileSinker()
        {
            _ofs.flush();

            // Note that any open file is automatically closed when the ofstream object is destroyed.
            // if(_ofs.is_open())
                // _ofs.close();
        }
        virtual bool run(const char* message, size_t size) override
        {
            // 1.写入文件
            _ofs.write(message, size);
            // 如果程序一直在循环进行FileSink，此时如果程序被中断，则文件会没来得及刷新缓冲区，写入的日志丢失
            // 2.刷新缓冲区。
            _ofs.flush();
            
            return _ofs.good();
        }
    private:
        std::string _file;
        std::ofstream _ofs; // 写入文件的句柄
    };

    /*控制台颜色输出（目前仅支持同步日志器，异步日志器调用该函数会导致颜色混乱）*/
    class ColorConsoleSinker : public Sinker
    {
        /* 根据ANSI转义，重新组织字符串 message，然后输出到标准输出。*/
        virtual bool run(const char *message, size_t size) override
        {
            // 扫描 message，找到日志等级，找得到，就根据等级添加颜色宏
            std::string s(message);
            std::string out;
            out.reserve(size + 25);
            if(s.find("[DEBUG]") != std::string::npos)
            {
                // 给字符串添加红色
                out += DEBUG_COLOR;
                out += message;
                out += DEBUG_COLOR;
            }
            else if(s.find("[INFO]") != std::string::npos)
            {
                out += INFO_COLOR;
                out += message;
                out += INFO_COLOR;
            }
            else if(s.find("[WARN]") != std::string::npos)
            {
                out += WARN_COLOR;
                out += message;
                out += WARN_COLOR;
            }
            else if(s.find("[ERROR]") != std::string::npos)
            {
                out += ERROR_COLOR;
                out += message;
                out += ERROR_COLOR;
            }
            else if(s.find("[FATAL]") != std::string::npos)
            {
                out += FATAL_COLOR;
                out += message;
                out += FATAL_COLOR;
            }
            else //没找到日志等级
            {
                out += message;
                std::cout.write(out.c_str(), out.size());
                return std::cout.good();
            }
            out += RESET_COLOR_TEXT;
            std::cout.write(out.c_str(), out.size());
            return std::cout.good();
        }
    };

    /*创建Sinker对象，并返回该对象的智能指针，采用工厂模式，以支持扩展不同日志输出目标*/
    struct SinkerCreator
    {
        template <typename SinkerType, typename... Args>
        static Sinker::ptr run(Args &&...args)
        {
            return std::make_shared<SinkerType>(std::forward<Args>(args)...);
        }
    };
}

#endif